package com.fanketly.musicdiary.drag_test


import android.util.Log
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.FastOutLinearInEasing
import androidx.compose.animation.core.tween
import androidx.compose.foundation.lazy.LazyListItemInfo
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.geometry.Offset
import com.fanketly.musicdiary.data.TAG
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch

class DragDropState internal constructor(
    val state: LazyListState,
    private val scope: CoroutineScope,
    private val onSwap: (Int, Int) -> Unit
) {
    //拖动的位置
    private var draggedDistance by mutableStateOf(0f)

    //开始拖动时item的位置
    private var draggingItemInitialOffset by mutableStateOf(0)
    internal val draggingItemOffset: Float
        get() = draggingItemLayoutInfo?.let { item ->
            //移动到那个位置的item
            val o = draggingItemInitialOffset + draggedDistance - item.offset
//            Log.i(TAG, "draggingItemOffset:${item}_${o} ")
            o
//            draggingItemInitialOffset - item.offset.toFloat()
        } ?: 0f

    //拖动的item
    private val draggingItemLayoutInfo: LazyListItemInfo?
        get() = state.layoutInfo.visibleItemsInfo
            .firstOrNull { it.index == currentIndexOfDraggedItem }

    internal var previousIndexOfDraggedItem by mutableStateOf<Int?>(null)
        private set
    internal var previousItemOffset = Animatable(0f)
        private set

    // 用于在拖动开始时获得初始偏移量
    private var initiallyDraggedElement by mutableStateOf<LazyListItemInfo?>(null)

    //拖动的item
    var currentIndexOfDraggedItem by mutableStateOf<Int?>(null)

    private val initialOffsets: Pair<Int, Int>?
        get() = initiallyDraggedElement?.let { Pair(it.offset, it.offsetEnd) }

    //拖动的item
    private val currentElement: LazyListItemInfo?
        get() = currentIndexOfDraggedItem?.let {
            state.getVisibleItemInfoFor(absoluteIndex = it)
        }


    fun onDragStart(offset: Offset) {
        state.layoutInfo.visibleItemsInfo
            .firstOrNull { item -> offset.y.toInt() in item.offset..(item.offset + item.size) }
            ?.also {
                currentIndexOfDraggedItem = it.index
                initiallyDraggedElement = it
                draggingItemInitialOffset = it.offset
            }
    }

    fun onDragInterrupted() {
        if (currentIndexOfDraggedItem != null) {
            previousIndexOfDraggedItem = currentIndexOfDraggedItem
//             val startOffset = draggingItemOffset
            scope.launch {
//                previousItemOffset.snapTo(startOffset)
                previousItemOffset.animateTo(
                    0f,
                    tween(easing = FastOutLinearInEasing)
                )
                previousIndexOfDraggedItem = null
            }
        }
        draggingItemInitialOffset = 0
        draggedDistance = 0f
        currentIndexOfDraggedItem = null
        initiallyDraggedElement = null
    }

    fun onDrag(offset: Offset) {
        draggedDistance += offset.y
//        Log.i(TAG, "DragDropColumn:onDrag:$draggedDistance ")
        initialOffsets?.let { (topOffset, bottomOffset) ->
            //初始偏移量+移动距离,因为top是顶部而触摸上拉的位移不是从顶部算的，所以不会完全覆盖才交换位置
            val startOffset = topOffset + draggedDistance
            val endOffset = bottomOffset + draggedDistance

            currentElement?.let { hovered ->
//                Log.i(TAG, "onDrag: ${
//                    state.layoutInfo.visibleItemsInfo
//                        .filterNot { item ->
//                            item.offsetEnd < startOffset || item.offset > endOffset || hovered.index == item.index
//                        }.firstOrNull { item ->
//                            val delta = (startOffset - hovered.offset)
////                        Log.i(TAG, "onDrag: $delta startOffset:${startOffset}  hovered:${hovered.offset}")
//                            when {
//                                delta > 0 -> (endOffset > item.offsetEnd)
//                                else -> (startOffset < item.offset)
//                            }
//                        }?.size
//                }"
//                )

                state.layoutInfo.visibleItemsInfo
                    .filterNot { item ->
                        item.offsetEnd < startOffset || item.offset > endOffset || hovered.index == item.index
                    }
                    .firstOrNull { item ->
                        val delta = (startOffset - hovered.offset)
//                        Log.i(TAG, "onDrag: $delta startOffset:${startOffset}  hovered:${hovered.offset}")
                        when {
                            delta > 0 -> (endOffset > item.offsetEnd)
                            else -> (startOffset < item.offset)
                        }
                    }
                    ?.also { item ->
                        draggingItemLayoutInfo?.let {
                            Log.i(TAG, "onDrag333: ${it.size}")
                            draggedDistance += it.size * 0.33f
                        }
                        currentIndexOfDraggedItem?.let { current ->

                            scope.launch {
                                onSwap.invoke(
                                    current,
                                    item.index
                                )
                            }
                        }
                        currentIndexOfDraggedItem = item.index

                    }
            }
        }
    }

    fun checkForOverScroll(): Float {
        return initiallyDraggedElement?.let {
            val startOffset = it.offset + draggedDistance
            val endOffset = it.offsetEnd + draggedDistance
            return@let when {
                draggedDistance > 0 -> (endOffset - state.layoutInfo.viewportEndOffset + 50f).takeIf { diff -> diff > 0 }
                draggedDistance < 0 -> (startOffset - state.layoutInfo.viewportStartOffset - 50f).takeIf { diff -> diff < 0 }
                else -> null
            }
        } ?: 0f
    }
}